package com.epower.tools;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.HexUtil;
import com.epower.tools.bean.AnalogChannelInfo;
import com.epower.tools.bean.DatRowBean;
import com.epower.tools.bean.PntDataBean;
import com.epower.tools.bean.StatusChannelInfo;
import org.apache.log4j.Logger;

import java.io.*;
import java.nio.ByteBuffer;
import java.util.*;

/**
 * @description: DAT文件读取
 * @author: LIU JUN
 * @create: 2021-03-21 22:58
 */
public class DatFileDataBase {
    static Logger logger = Logger.getLogger(DatFileDataBase.class);
    static final String[] HighLetter = {"A", "B", "C", "D", "E", "F"};
    static Map<String, Integer> hexCharIntMap = new HashMap<>();

    static {
        for (int i = 0; i <= 9; i++) {
            hexCharIntMap.put(i + "", i);
        }
        for (int j = 10; j < HighLetter.length + 10; j++) {
            hexCharIntMap.put(HighLetter[j - 10], j);
        }
    }

    private final String datFileLocation;


    /**
     * 采样记录
     */
    private List<DatRowBean> dataRows = null;

    /**
     * 以字节为单位读取文件，常用于读二进制文件，如图片、声音、影像等文件。
     */
    public DatFileDataBase(String datFileLocation) {
        this.datFileLocation = datFileLocation;

    }

    final static int FOUR_BYTES = 4;
    final static int TWO_BYTES = 2;

    public void parse(CfgFile cfgFile) throws Exception {
        Integer anaCounts = cfgFile.getAnalogChannelInfoList().size();
        Integer statusCounts = cfgFile.getStatusChannelInfoList().size();
        //每个采样记录的字节数
        Integer oneRowBytes = anaCounts * 2 + Double.valueOf(Math.ceil(2 * statusCounts / 16)).intValue() + FOUR_BYTES + FOUR_BYTES;
        File file = new File(datFileLocation);
        logger.debug(String.format("字节总数:%d,模拟通道数量:%d,状态通道数量:%d,每个采样要求的字节数:%d", datFileLocation.length(), anaCounts, statusCounts, oneRowBytes));

        logger.debug("文件:" + file.getAbsolutePath() + ",Size:" + file.length() + "Bytes,LastUpdateTime:" + DateUtil.format(new Date(file.lastModified()), "yyyy-MM-dd HH:mm:ss.SSS"));
        this.dataRows = new ArrayList<>();
        InputStream inputStream = new FileInputStream(datFileLocation);
        Integer totalRows = Double.valueOf(Math.ceil(file.length() / oneRowBytes)).intValue();

        //开始记录数据的时间微秒数
        long startTimeMicroSeconds = cfgFile.toMicroSeconds(cfgFile.getFirstTimeText());

        for (int rowNo = 0; rowNo < totalRows; rowNo++) {
            //按每次采样开始读取
            int readBytes = 0;
            DatRowBean datRowBean = new DatRowBean();
            //读取采样编号
            byte[] buffer = new byte[FOUR_BYTES];
            readBytes += inputStream.read(buffer);
            datRowBean.setSampleIndex(bytesToInt(buffer));
            //读取时标
            buffer = new byte[FOUR_BYTES];
            readBytes += inputStream.read(buffer);
            //使用时间倍率因子计算
            long timeStamp = startTimeMicroSeconds + Double.valueOf(Long.valueOf(bytesToInt(buffer)) * cfgFile.getTimemultDouble()).longValue();
            //时标已经使用时间倍率因子及dat存储的timestamp乘积加上开始时间计算过了
            datRowBean.setTimeStamp(timeStamp);

            //读取模拟通道数据
            int idx = 0;
            for (AnalogChannelInfo analogChannelInfo : cfgFile.getAnalogChannelInfoList()) {
                buffer = new byte[TWO_BYTES];
                readBytes += inputStream.read(buffer);
                //原始采样值*系数
                datRowBean.addData(bytesToInt(buffer) * analogChannelInfo.chanMultiRate());
                //转向下一个点
                idx++;
            }
            //读取状态通道数据
            buffer = new byte[oneRowBytes - readBytes];
            readBytes += inputStream.read(buffer);
            idx = 0;
            List<Integer> byteBitStatusList = getByteArrayStatusList(buffer);
            for (StatusChannelInfo statusChannelInfo : cfgFile.getStatusChannelInfoList()) {
                datRowBean.addData(byteBitStatusList.get(idx++));
            }
            logger.debug(String.format("行%d,读取字节数:%d", rowNo, readBytes));
            this.dataRows.add(datRowBean);
        }
    }


    /**
     * 字节数组转换为整数
     *
     * @param bytes
     * @return
     */
    public Integer bytesToInt(byte[] bytes) {
        String hexText = reverseHex(HexUtil.encodeHexStr(bytes).toUpperCase());
        return covert(hexText);
    }

    /**
     * 将byte转换为一个长度为8的byte数组，数组每个值代表bit
     */

    public static List<Integer> getByteArrayStatusList(byte[] bArray) {
        List<Integer> byteBitStatusList = new ArrayList<>();
        for (byte b : bArray) {
            for (int i = 0; i < 8; i++) {
                if (((b >> i) & 0x01) == 1) {
                    byteBitStatusList.add(1);
                } else {
                    byteBitStatusList.add(0);
                }
            }
        }
        return byteBitStatusList;
    }

    /**
     * 转换高地位
     *
     * @param hex
     * @return
     */
    public static String reverseHex(String hex) {
        char[] charArray = hex.toCharArray();
        int length = charArray.length;
        int times = length / 2;
        for (int c1i = 0; c1i < times; c1i += 2) {
            int c2i = c1i + 1;
            char c1 = charArray[c1i];
            char c2 = charArray[c2i];
            int c3i = length - c1i - 2;
            int c4i = length - c1i - 1;
            charArray[c1i] = charArray[c3i];
            charArray[c2i] = charArray[c4i];
            charArray[c3i] = c1;
            charArray[c4i] = c2;
        }
        return new String(charArray);
    }

    public static Integer covert(String content) {
        int number = 0;

        String[] str = new String[content.length()];
        for (int i = 0; i < str.length; i++) {
            str[i] = content.substring(i, i + 1);
        }
        for (int i = 0; i < str.length; i++) {
            number += hexCharIntMap.get(str[i]) * Math.pow(16, str.length - 1 - i);
        }
        return number;
    }

    /**
     * 查询指定点在特定的值
     *
     * @param rowNo
     * @return
     */
    public Object queryRowItemValue(Integer rowNo, Integer colNum) {
        if (rowNo > -1 && rowNo < this.dataRows.size() && colNum > -1) {
            DatRowBean rowValue = this.dataRows.get(rowNo);
            if (colNum < rowValue.getValues().size()) {
                return rowValue.getValues().get(colNum);
            }
        }
        return null;
    }

    /**
     * 查询指定点在特定的对象
     *
     * @param rowNo
     * @return
     */
    public PntDataBean queryRowItem(Integer rowNo, CfgFile cfgFile, String chId) {
        int colNum = cfgFile.findValPosition(chId);
        if (rowNo > -1 && rowNo < this.dataRows.size() && colNum > -1) {
            DatRowBean rowValue = this.dataRows.get(rowNo);
            if (colNum < rowValue.getValues().size()) {
                return new PntDataBean(chId, rowValue.getTimeStamp(), rowValue.getValues().get(colNum));
            }
        }
        return null;
    }

    public List<DatRowBean> getDataRows() {
        return dataRows;
    }

    /**
     * 返回全部的采样时间标签
     *
     * @return
     */
    public List<Long> getAllTimeStamp() {
        List<Long> allTimes = new ArrayList<>();
        for (DatRowBean datRowBean : dataRows) {
            allTimes.add(datRowBean.getTimeStamp());
        }
        return allTimes;
    }

    /**
     * 获取最大值
     *
     * @param cfgFile
     * @param chId
     * @return
     */
    public PntDataBean getMaxValue(CfgFile cfgFile, String chId) {
        return getMinMaxValue(cfgFile, chId, true);
    }

    /**
     * 获取最小值
     *
     * @param cfgFile
     * @param chId
     * @return
     */
    public PntDataBean getMinValue(CfgFile cfgFile, String chId) {
        return getMinMaxValue(cfgFile, chId, false);
    }

    private PntDataBean getMinMaxValue(CfgFile cfgFile, String chId, boolean isMax) {
        int colNum = cfgFile.findValPosition(chId);
        if (colNum < 0) {
            return null;
        }
        if (this.dataRows.isEmpty()) {
            return null;
        }
        Long timeStamp = this.dataRows.get(0).getTimeStamp();
        Double firstValue = (Double) this.dataRows.get(0).getValues().get(colNum);
        for (DatRowBean datRowBean : this.dataRows) {
            Double tmp = (Double) datRowBean.getValues().get(colNum);
            if (isMax) {
                if (tmp.compareTo(firstValue) > 0) {
                    firstValue = tmp;
                    timeStamp = datRowBean.getTimeStamp();
                }
            } else {
                if (tmp.compareTo(firstValue) < 0) {
                    firstValue = tmp;
                    timeStamp = datRowBean.getTimeStamp();
                }
            }

        }
        return new PntDataBean(chId, timeStamp, firstValue);
    }
}
